//
// Event.m
// Copyright (c) 1988, 1989, 1990 by Jiro Nakamura 
// All rights reserved
//
// Implementation definition of class Event. Event handles 
// the database management of Cassandra.
//
// RCS Information
// Revision Number->	$Revision: 2.10 $
// Last Revised->	$Date: 91/01/27 15:35:26 $
//
static char rcsid[] = "$Id: Event.m,v 2.10 91/01/27 15:35:26 jiro Exp Locker: jiro $";

#import "Event.h"
#import <objc/objc.h>
#import <strings.h>
#import <appkit/Panel.h>       /* for NXRunAlertPanel */
#import "cass.h"	
#import "calendar.h"
#import "Global.h"
#import "misc.h"
#import <sys/types.h>
#import <sys/stat.h>
    
// Defining DEBUG for Event can sometimes get very screen messy
// since Cassandra uses it so often
// #undef DEBUG

int fgeti( int *i , 	FILE *fp)		
{
	char buffer[10];
	if( fgets(buffer, 10, fp) == NULL)
		return -1;
	*i = atoi(buffer);
	return 0;
}
		

@implementation Event

+ newAt:(const char*) eFile
{
	self = [super new];
	strcpy(eventFile,eFile);

	priority = 50;
	destroy = snoozeNo = snoozeInt =0;
	anniversary =  annvSpecial = 0;
	present = next = previous = 0;
	showMessage = TRUE;
	playAlarm = TRUE;
	
	strcpy(alarmSound, ALARMSOUND);
	strcpy(msg,"empty");
	return self;
}

+ new
{
	fprintf(stderr,"This should never be called!");
	self = [super new];
	return self;
}

- free
{
#ifdef DEBUG
	fprintf(stderr,"Event.m:Free ->Closing event!\n");
#endif
	[super free];
	return nil;
}

//
//
// Messages that affect how the file is stored on disk
//
//

- default: sender
{
	present =  0;
	previous = next = -1;
	destroy =  1;
	anniversary = annvSpecial = snoozeInt = snoozeNo = priority = 0;
	ts.tm_sec =  ts.tm_min =  ts.tm_hour = 0;
	ts.tm_mday = ts.tm_mon =  ts.tm_year = 0;
	ts.tm_wday =  ts.tm_yday =  ts.tm_isdst = 0;
	
	strcpy(msg, "");
	strcpy(alarmSound,[global alarmSound]);
	return self;
}


- readEvent: (EFileLink) here		/* read itself from the event file */
{
	FILE * source;
	static char errormsg[128], buffer[10];
	char *tp;	// temporary pointer
	struct stat statBuf;
	
	#ifdef DEBUG
		fprintf(stderr,	"------------------------\n"
					"[Event readEvent: %d]\n",here);
	#endif

	if( here < 0)
		{
		NXRunAlertPanel("File Seek Error", 
				"Cannot seek to a negative number.",
		 		"OK",NULL,NULL);
		strcpy(msg, "Illegal event/link value....");
		return nil;
		}
		
	/* Let's first check to make sure we aren't reading an empty file */
	stat( eventFile, &statBuf);
	if( statBuf.st_size < 100)	// It can not be this short
		{
		strcpy(msg, CASSANDRAVERSION);
		next = previous = 0;
		present = here;					
		[self writeEvent: here];
		fprintf(stderr, "%s: Event list not initialized. "
			"Initializing....\n",
			PROGNAME);
		return nil;
		}

	sprintf(errormsg, "Error occured while trying to read event file %s", 
		eventFile);
	source = fileOpen(eventFile, "r+",errormsg);
	
	sprintf(errormsg, 	"An fatal error occured when we were trying "
				"to seek to event number %d for reading event "
				"file %s.\n This program has crashed.", 
				here, eventFile);
	if( fileSeek(source, here, errormsg) == NULL)
		{
		next = previous = -1;
		present = here;
		return nil;
		}
	
	present = fgetnum(source);
	if( present != here)
		{
		fprintf(stderr, 
			"%s: Uh oh.... File header gives %d instead of %d.\n"
			"%s: We've most probably run out off the end of %s.\n"
			"%s: Not to worry. :-) I'll extend it.\n",
			PROGNAME ,present, here,
			PROGNAME, [global eventFile],
			PROGNAME);
		next = previous = -1;
		present = here;
		fclose(source);
		return nil;
		}
	else
		{
		previous = fgetnum(source);
		next = fgetnum(source);
		}

	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	
	fgeti( &priority, source);
	fgeti( &destroy, source);
	fgeti( &anniversary, source);
	fgeti( &snoozeNo, source);
	fgeti( &snoozeInt, source);
	
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	
	fgeti( &ts.tm_sec, source);
	fgeti( &ts.tm_min, source);
	fgeti( &ts.tm_hour, source);
	fgeti( &ts.tm_mday, source);
	fgeti( &ts.tm_mon, source);
	fgeti( &ts.tm_year, source);
	fgeti( &ts.tm_wday, source);
	fgeti( &ts.tm_yday, source);
	fgeti( &ts.tm_isdst, source);
	
	fgets( alarmSound, 100, source);	// We gotta strip the final
	tp = rindex( alarmSound, '\n');		// newline and replace it with
	if( tp != NULL)				// a NULL character.
		*tp = '\0';
	
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);
	fgets( buffer, 10, source);

	fgets(msg,MESSAGE_SIZE,source);
				
	ts.tm_wday = wday(ts.tm_mday, ts.tm_mon+1, 1900+ts.tm_year);
	ts.tm_yday = yday(ts.tm_mday, ts.tm_mon+1, ts.tm_year+1900);
	
	playAlarm = TRUE;
	deleteSound = FALSE;
	annvSpecial = 0;
	
	if( alarmSound[0] == '-')
		{
		char buf[255];
		
		strcpy(buf, alarmSound);
		strcpy(alarmSound, buf+1);
		playAlarm = FALSE;
		}
	else
		{
		if( alarmSound[0] == '?')
			{
			char buf[255];
			strcpy(buf, alarmSound);
			strcpy(alarmSound, buf+1);
			deleteSound = TRUE;
			}
		}
	
	if( msg[0] == ';')
		{
		char buf[MESSAGE_SIZE];
		
		strcpy(buf, msg);
		strcpy(msg, buf+1);
		showMessage = FALSE;
		}
	else
		showMessage = TRUE;
			
	fclose(source);
	return self;
}
				
- firstEvent
{
	#ifdef DEBUG
		fprintf(stderr,"Reading in the first event.\n");
	#endif

	[self readEvent : 0];
	if( [self next] == 0)
		return self;
	[self readEvent : [self next]];
	return self;
}	
	
- writeEvent: (EFileLink) here	
/* write itself into the event file using present as its index */
{
	FILE *source;
	char errormsg[256], bufAlarm[256], bufMessage[256];
		
	if( here < 0)
		{
		NXRunAlertPanel( "Write Error", 
				"We can't write to a negative event number.",
				"OK",NULL,NULL);
		return nil;
		}
		
	#ifdef DEBUG
		fprintf(stderr,	"------------------------\n"
				"[Event writeEvent: %d]: Opening event with "
				"<%s>!\n",here, eventFile);
	#endif

	sprintf(errormsg, "Fatal error when trying to write to event file %s", 
		eventFile);
	source = fileOpen(eventFile, "r+",errormsg);
	
	sprintf(errormsg, "%s: An fatal error occured when we were trying to "
			"seek to event number %d for writing in event file %s.\n"
			" This program has crashed.", PROGNAME, here, eventFile);
	fileSeek(source, here, errormsg);
	
	present = here;
	
	if( ts.tm_year > 1900)
		ts.tm_year -= 1900;

	if( playAlarm == FALSE)
		{		
		strcpy(bufAlarm, "-");
		strcat(bufAlarm,alarmSound);
		}
	else
		{
		if( deleteSound == TRUE )
			{
			strcpy(bufAlarm, "?");
			strcat(bufAlarm,alarmSound);
			}
		else
			strcpy(bufAlarm, alarmSound);
		}

	if( showMessage == FALSE)
		{		
		strcpy(bufMessage, ";");
		strcat(bufMessage, msg);
		}
	else
		strcpy(bufMessage, msg);


	if(fprintf( source, 		"%d\n%d\n%d\n"
					"\n\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n"
					"\n\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n%d\n"
					"%d\n%d\n%d\n"
					"%s\n\n"
					"\n\n\n"
					"%s\n",
					present, previous, next,
					priority,destroy, anniversary,
					snoozeNo, snoozeInt,
					ts.tm_sec, ts.tm_min, ts.tm_hour,
					ts.tm_mday, ts.tm_mon, ts.tm_year,
					ts.tm_wday, ts.tm_yday, ts.tm_isdst,
					bufAlarm, bufMessage) == -1)
		{
		fprintf(stderr, "%s: Event.m:   Error occured writing file.",
			 PROGNAME);
		strcpy(msg, "Error occured in writing out Elink number.\n\n");
		fclose(source);
		return nil;
		}
	
		#ifdef DEBUG
			fprintf(stderr, "%s: Wrote out Efilelink.\n  Here = %d, "
				"Previous = %d, Next=%d\n", PROGNAME,
				here, previous, next);
			fprintf(stderr, "%s: Date:   time = %d:%d, day= %d, "
				"month = %d, year = %d.\n\n",PROGNAME,
				ts.tm_hour, ts.tm_min, ts.tm_mday, ts.tm_mon, 
				ts.tm_year);
			fprintf(stderr,"%s: AlarmSound =%s\n", PROGNAME, bufAlarm);
		#endif
		
	fclose(source);
	return self;
}

- (EFileLink) insertEvent
{
	return [self insertEventFrom : 1];
}



/* insert itself into the file with current day as index */
/* and starting from <here> in searching for a open deleted space */
- (EFileLink) insertEventFrom : (EFileLink) here
{
	id event, step;
	int loop;
	

	// First things first, we must check the format of our time structure  
	// since we have given other functions full freedom to scew it up

	fixTmStructure( &ts);
			
	event = [Event newAt:eventFile];
	step = [Event newAt:eventFile];
	[event readEvent:0];
	for( [event readEvent: [event next]]; 
	      [event present] != 0 && timeCompare([event time], &ts) < 1;
	      [event readEvent:[event next]])
	      	{
	      	#ifdef DEBUG
			fprintf(stderr, "From %d looping onto %d\n",[event present],[event next]);
		#endif
		}
	#ifdef DEBUG	
		fprintf(stderr, "inserting between %d <- event -> %d.\n",[event previous], [event present]);
	#endif
	
	previous = [event previous];
	next = [event present];
	
	[step setNext: 0];
	for( loop = here; !([step next] == -1 && [step previous] == -1); [step readEvent: loop++])
		{
		#ifdef DEBUG
			fprintf(stderr, "reading at %d.\n",loop);
		#endif
		}
		
	present = [step present];
	
	[self writeEvent: present];
	
	[step readEvent: previous];
	[step setNext : present];
	[step writeEvent:previous];
	
	[step readEvent: next];
	[step setPrevious : present];
	[step writeEvent:next];
	
	[step free];
	[event free];
	#ifdef DEBUG
		fprintf(stderr,"successfully inserted %d <- %d -> %d\n\n",[self previous],[self present], [self next]);
	#endif

	
	return( [self present] );
}

/* delete itself from the file with present as index */
- deleteEvent  : (EFileLink) here 
{
	id previousEvent, nextEvent;
	
	#ifdef DEBUG
		fprintf(stderr, "Deleting <%d>\n\n",here);
	#endif
	
	if( here <= 0)
		{
		NXRunAlertPanel(NULL,"You cannot delete link 0 or below. OK?",
				 "Shucks...",NULL,NULL);
		fprintf(stderr,"Can't delete the header.  Here = %d.\n\n",
			 here);
		return nil;
		}
	
	previousEvent= [Event newAt: eventFile];
	nextEvent = [Event newAt:eventFile];
	
	[previousEvent readEvent  :here];
	if( ( [previousEvent previous] == -1  )   || 
	    ( [previousEvent next]  == -1  )		)
		{
		/*   link was already deleted, so we can't delete it..... */
		strcpy(msg, "Can't delete link because it's already been "
			"deleted!?!\n\n");
		return nil;
		}
	
	/* Set the previous_link of the next link */
	/* to the one of the previous one */					
	[nextEvent readEvent :[previousEvent next]];	
	[nextEvent setPrevious  : [previousEvent previous]];			
	[nextEvent writeEvent  :[previousEvent next]];	
	
	/* do the same confusing spiel for previous */
	[nextEvent readEvent  :[previousEvent previous] ];	
	[nextEvent setNext  : [previousEvent next]];
	[nextEvent writeEvent  :[previousEvent previous] ];
	
	/* cancel out the header to finally kill it */
	/* and write it out */
	[previousEvent setNext  : -1];	
	[previousEvent setPrevious  :-1];	
	[previousEvent writeEvent  :here];	
	
	#ifdef DEBUG
		fprintf(stderr, "Record %d successfully deleted.\n\n", 
			here);
	#endif
	
	[previousEvent free];
	[nextEvent free];
	return self;
}

/* delete if it is not an anniversary event, update otherwise */
- (int) murderEvent : (EFileLink) here
{
	Event *event;
	
	#ifdef DEBUG
		fprintf(stderr,"Arrggh! Help! I'm getting murdered! "
			"(says Event <%d>)\n\n",
			here);
	#endif
	
	event = [Event newAt :eventFile];
	[event readEvent : here];		//	Read the event in
	[event deleteEvent : here];		//	Delete the event
	
	if( (int) ([event anniversary]/100) == 0)		// No anniversary
		return -1;
		
	[event setTime: fixAnniversary( [event time], [event anniversary], 
		[event annvSpecial])];
	// 	Fix the next event time and reinsert it
								
	// Use the same EFilelink space that we deleted, this saves 
	// us time and maybe some confusion 
	return( [event insertEventFrom:here]  );
}


- (EFileLink) present
{
	return present;
}

- (EFileLink) previous
{
	return previous;
}

- (EFileLink) next
{
	return next;
}

- setPresent : (EFileLink) apresent
{
	present = apresent;
	return self;
}
- setPrevious  : (EFileLink) aprevious
{
	previous =aprevious;
	return self;
}

- setNext  : (EFileLink) anext
{
	next = anext;
	return self;
}

- (struct tm *) time
{
	return &ts;
}

- setTime : (struct tm *) time
{	
	ts = *time;
	return self;
}

- setMday : (int) x
{	
	ts.tm_mday = x;
	return self;
}

- (int) mday;
{
	return( ts.tm_mday);
}

- setMon: (int) x
{
	ts.tm_mon = x;
	return self;
}

- (int) mon
{
	return (ts.tm_mon);
}

- setYear: (int) x
{
	ts.tm_year = x;
	return self;
}

- (int) year
{
	return (ts.tm_year);
}

- setHour: (int) x
{
	ts.tm_hour = x;
	return self;
}

- (int) hour
{
	return (ts.tm_hour);
}

- setMin: (int) x
{
	ts.tm_min = x;
	return self;
}

- (int) min
{
	return(ts.tm_min);
}

- setSec: (int) x
{
	ts.tm_sec = x;
	return self;
}

- (int) sec
{
	return(ts.tm_sec);
}

- (int) destroy
{
	return destroy;
}

- setDestroy : (int) dst
{
	destroy = dst;
	return self;
}


- (int) anniversary
{
	return anniversary;
}

- setAnniversary : (int) anv
{
	anniversary = anv;
	return self;
}


- (int) annvSpecial
{
	return annvSpecial;
}

- setAnnvSpecial : (int) anvS
{
	annvSpecial = anvS;
	return self;
}

- (int) snoozeNo
{
	return snoozeNo;
}

- setSnoozeNo : (int) sn
{
	snoozeNo = sn;
	return self;
}


- (int) snoozeInt
{
	return snoozeInt;
}

- setSnoozeInt : (int) si
{
	snoozeInt = si;
	return self;
}

- (int) priority
{
	return priority;
}

- setPriority : (int) si
{
	priority = si;
	return self;
}

- (char *) alarmSound
{
	return alarmSound;
}

- setAlarmSound : (char *) aS
{	
	strcpy( alarmSound, aS);
	return self;
}

- setPlayAlarm: (BOOL) aFlag
{
	playAlarm = aFlag;
	return self;
}


- (BOOL) playAlarm
{
	return playAlarm;
}

- setShowMessage: (BOOL) aFlag
{
	showMessage = aFlag;
	return self;
}


- (BOOL) showMessage
{
	return showMessage;
}


- (char *) message
{
	return msg;
}


- (BOOL) deleteSound
{
	return deleteSound;
}

- setDeleteSound: (BOOL) aFlag
{
	deleteSound = aFlag;
	return self;
}

- setMessage : (char *) message
{	
	strcpy( msg, message);
	return self;
}

- setWday : (int) wd
{
	ts.tm_wday = wd;
	return self;
}

- setYday : (int) yd
{
	ts.tm_yday = yd;
	return self;
}

- (int) wday
{
	return ts.tm_wday;
}

- (int) yday
{
	return ts.tm_yday;
}
@end		


struct tm *fixAnniversary( struct tm *time, int mode, int sub)
{
	#ifdef DEBUG
	fprintf(stderr,"Fixing weekly special: %s, %d, %d\n", ascMyTime(time, YES, NO), 
		mode, sub);
	#endif

	switch( (int) mode  / 100)      
		{
		/* Don't worry about overflows as we add intervals to */
		/* our dates, insertEvent is smart enough to handle it */
		case 0:
			return time;	/* No anniversary */
		case 1:			/* If it is a day anniversary */
			time->tm_mday+=  (mode - 100);
			break;
		case 2:			/* Weekly anniversary */
			time -> tm_mday +=  ((mode - 200) * 7 );
			break;
		case 3:			/* Monthly anniversary */
			time-> tm_mon += (mode - 300);
			break;
		case 4:			/* Yearly anniversary */
			time -> tm_year +=  (mode - 400);
			break;
		case 5:			/* Special weekly */
		default:
			 fprintf(stderr,
				"%s: I do not recognize anniversary event: %d.\n", 
				PROGNAME,
			 	mode);
			 return time;
		}

	fixTmStructure( time);
	return time;
}


